#include "Lily_help.h"

typedef struct
{
    float PerdSec;
    int32_t count;
    Li_List cmds;
} Schedule_item_t;
typedef Schedule_item_t *Schedule_item;

static Li_List sch_items = NULL;               // List<Schedule_item_t>
int32_t sch_do_task(Schedule_item item) // cmds)
{
    Li_List cmds = item->cmds;

    if (item->count == 0)
    {
        li_remove_timer_arg((TasksArg_def)sch_do_task, item);
        return 0;
    }
    if (item->count > 0)
    {
        item->count--;
    }
    int32_t n = cmds->count;
    str *ss = list_content(cmds, str);
    int32_t i;
    for (i = 0; i < n; i++)
    {
        lily_do(ss[i]);
    }

    if (item->count == 0)
    {
        li_remove_timer_arg((TasksArg_def)sch_do_task, item);
    }
    return 0;
}
int32_t add_new_sch_item(float sec, int32_t run_times, int32_t n, char *cmds[])
{
    Schedule_item_t new_item = {sec, run_times};
    new_item.cmds = new_list(sizeof(str), 1);
    if (new_item.cmds == NULL)
    {
        li_error("mem out", -__LINE__);
    }
    int32_t i;
    for (i = 0; i < n; i++)
    {
        str s = new_string_by(cmds[i]);
        if (s == NULL)
        {
            li_error("mem out", -__LINE__);
        }
        if (li_add(new_item.cmds, s) < 0)
        {
            free(s);
            li_error("mem out", -__LINE__);
        }
    }
    list_reduction(new_item.cmds);
    i = li_add(sch_items, new_item);
    if (i < 0)
        return -1;
    Schedule_item items = list_content(sch_items, Schedule_item_t);

    li_new_timer_arg((TasksArg_def)sch_do_task, Second(sec), cast(items + i, void *));
    return 0;
}
int32_t remove_sch_item(int32_t index)
{
    Schedule_item items = list_content(sch_items, Schedule_item_t);
    if (index >= sch_items->count || index < 0)
    {
        li_error("index out", -__LINE__);
    }
    li_remove_timer_arg((TasksArg_def)sch_do_task, items + index);

    int32_t n = items[index].cmds->count;
    int32_t i;
    str *ss = list_content(items[index].cmds, str);
    for (i = 0; i < n; i++)
    {
        free(ss[i]);
    }
    delete_list(items[index].cmds);
    list_remove_at(sch_items, index);
    return 0;
}
void sch_list_all()
{
    char tx[TX_len];
    Schedule_item items = list_content(sch_items, Schedule_item_t);
    int32_t n = sch_items->count;
    int32_t i;

    for (i = 0; i < n; i++)
    {
        sprintf(tx, "%d):%.2fs %dc,", i, items[i].PerdSec, items[i].count);
        lily_out(tx);
        int32_t n = items[i].cmds->count;
        str *ss = list_content(items[i].cmds, str);
        int32_t j;
        for (j = 0; j < n; j++)
        {
            lily_out(ss[j]);
            lily_out(";");
        }
        lily_out("\n");
    }
}
// add timer to exec one cmd
// sch 10Hz "blink 3"
// sch 0.5s "beep 0.1"
// sch list
// sch del 1
const char *sch_annotation = "schedule task:\n"
                             "sch period cmds\n"
                             "sch d[a] [index]\n"
                             "sch c index newCount\n"
                             "sch -r:reduction memory";
int32_t cmd_schedule(int32_t n, char *arg[])
{
    if (n <= 1)
    {
        sch_list_all();
        return sch_items->count;
    }
    if (isD(arg[1][0]))
    {
        assert_msg(n >= 3, "bad arg:sch period[s|h]count cmd ...");
        int32_t i = 0; //
        while (isD(arg[1][i]) || arg[1][i] == '.')
            i++;
        char perd_type = 's';
        int32_t run_times = -1;
        if (toLower(arg[1][i]) == 's')
        {
            perd_type = 's';
        }
        else if (toLower(arg[1][i]) == 'h' /*&& toLower(arg[1][i + 1]) == 'z'*/)
        {
            perd_type = 'h';
        }
        if (isD(arg[1][i + 1]))
        {
            if (str_is_numeric(arg[1] + i + 1))
            {
                run_times = atoi(arg[1] + i + 1);
            }
        }

        arg[1][i] = '\0';
        float time = str_to_float(arg[1]);
        if (perd_type == 'h')
            time = 1.0f / time;

        return add_new_sch_item(time, run_times, n - 2, arg + 2);
    }
    if (arg[1][0] == 'd')
    {
        int32_t ind;
        if (arg[1][1] == 'a')
        {
            for (ind = sch_items->count - 1; ind > -1; ind--)
                remove_sch_item(ind);
            return 0;
        }
        int32_t i = 2;
        for (; i < n; i++)
        {
            ind = atoi(arg[i]);
            remove_sch_item(ind);
        }
        return sch_items->count;
    }

    if (arg[1][0] == 'c') // sch c index newCount
    {

        int32_t ind;
        ind = atoi(arg[2]);
        int32_t newCount = atoi(arg[3]);

        Schedule_item items = list_content(sch_items, Schedule_item_t);
        int32_t n = sch_items->count;
        assert_msg(n > ind, "index error");
        if (newCount == items[ind].count)
            return 0;

        if (items[ind].count == 0)
        {
            items[ind].count = newCount;
            li_new_timer_arg((TasksArg_def)sch_do_task, Second(items[ind].PerdSec), cast(items + ind, void *));
        }
        else
        {
            items[ind].count = newCount;
            if (newCount == 0)
            {
                li_remove_timer_arg((TasksArg_def)sch_do_task, items + ind);
            }
        }
        return sch_items->count;
    }
    if (strcmp(arg[1], "-r") == 0)
    {
        list_reduction(sch_items);
        return 0;
    }
    lily_out(sch_annotation);
    return 0;
}
static void _on_mem_reduction(void *private, void *msg)
{
    list_reduction(sch_items);
}
void li_sch_init()
{
    sch_items = new_li(Schedule_item_t);
    if (!sch_items)
    {
        return;
    }
    public_a_cmd_link_doc("sch", cmd_schedule, sch_annotation);
    event_register_to_tag("memReduction", NULL, _on_mem_reduction);
}
